package tools;



/* * ZipTools.java * * Created on 12 octobre 2005, 08:56 */
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.Adler32;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.CheckedOutputStream;
import java.util.zip.Checksum;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;   



/** * * @author Yann D'ISANTO 
 * Cette classe fournit diverses méthodes pour manipuler les archives zip.. 
 * */ 
final public class ZipTools {  

	private ZipTools() {};
	public static final int BUFFER_SIZE = 2048;
	public static final Checksum CHECK_NONE = null;
	public static final Checksum CHECK_ADLER32 = new Adler32();
	public static final Checksum CHECK_CRC32 = new CRC32();
	
	
	/** * Décompresse l'archive zip spécifiée dans le répertoire contenant la dite 
	 * archive. Les fichiers existants sont écrasés. 
	 * @param file L'archive zip à décompresser. */
	public static final void unzip(File file)  throws IOException {
	
		String path = file.getAbsolutePath();
		unzip(file, path.substring(0, path.lastIndexOf(File.separatorChar)));
	}  
	
	
	/** * Décompresse l'archive zip spécifiée dans le répertoire contenant la dite 
	 * archive en utilisant le checksum spécifié. Les fichiers existants sont écrasés. 
	 * @param file L'archive zip à décompresser. 
	 * @param checksum Le checksum à utiliser lors de la décompression. */
	public static final void unzip(File file, Checksum checksum)  throws IOException {
	  
		String path = file.getAbsolutePath();
		unzip(file, path.substring(0, path.lastIndexOf(File.separatorChar)), checksum);
	}  
	
	
	/** * Décompresse l'archive zip spécifiée dans le répertoire spécifié. 
	 * Les fichiers existants sont écrasés. 
	 * @param file L'archive zip à décompresser. 
	 * @param destinationPath Le répertoire dans lequel l'archive zip doit être décompressée. 
	 */  
	public static final void unzip(File file, String destinationPath)  throws IOException {
	
		unzip(file, destinationPath, true, CHECK_NONE);
	}  
	
	
	/** * Décompresse l'archive zip spécifiée dans le répertoire spécifiéen 
	 * utilisant le checksum spécifié. Les fichiers existants sont écrasés. 
	 * @param file L'archive zip à décompresser. 
	 * @param destinationPath Le répertoire dans lequel l'archive zip doit être décompressée. 
	 * @param checksum Le checksum à utiliser lors de la décompression. */
	public static final void unzip(File file, String destinationPath, Checksum checksum)  throws IOException {
		unzip(file, destinationPath, true, checksum);
	}  
	
	
	
	/** * Décompresse l'archive zip spécifiée dans le répertoire spécifié en 
	 * utilisant le checksum spécifié. Permet de spécifier s'il faut ou non 
	 * écraser les fichiers existants. * @param file L'archive zip à décompresser. 
	 * @param destinationPath Le répertoire dans lequel l'archive zip doit être décompressée. 
	 * @param force Spécifie s'il faut écraser les fichiers existants. 
	 * @param checksum Le checksum à utiliser lors de la décompression. */
	public static final void unzip(File file, String destinationPath, boolean force, Checksum checksum) throws IOException {
	
		ZipFile zipfile = new ZipFile(file); byte [] buf = new byte[BUFFER_SIZE];
		FileInputStream fis = new FileInputStream(zipfile.getName());
		CheckedInputStream cis = (checksum == null) ? null : new CheckedInputStream(fis, checksum);
		BufferedInputStream bis = new BufferedInputStream((cis != null) ? cis : fis);
		ZipInputStream zis = new ZipInputStream(bis);
		ZipEntry entry = null;
	
		try {  while((entry = zis.getNextEntry()) != null) {
				File f = new File(destinationPath + File.separatorChar + entry.getName());
				if(entry.isDirectory()) {  
					if(!f.exists()) {  
						f.mkdirs();
					 }  
					continue; 
				}  
	
				if(f.exists()) {  
					if(force) {  
						f.delete(); 
					} else {  
						continue; 
					}  
				}  
	
				f.createNewFile();
				FileOutputStream fos = new FileOutputStream(f);
				BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER_SIZE);
				int nbRead;
	
				try {  
					while ((nbRead = zis.read(buf)) > 0) {
						bos.write(buf, 0, nbRead);
					}  
				} finally {  
	
					bos.flush();
					bos.close();
					fos.close(); 
				}  
			}  
		} 
		finally {  
			zis.close(); 
			bis.close(); 
			if(cis != null) {  
				cis.close();
			}  
			fis.close(); 
		}  
	}  
	
	
	
	/** * Compresse les fichiers spécifiés dans l'archive zip spécifiée. 
	 * @param zipFile L'archive zip dans laquelle seront compressés les 
	 * fichiers. Ce fichier doit déjà avoir été créé. 
	 * @param files fichiers à compresser. */  
	public static final void zip(File zipFile, File ...files)  throws IOException {  
	
		zip(zipFile, Deflater.BEST_COMPRESSION, files);
	}  
	
	
	/** * Compresse les fichiers spécifiés dans l'archive zip spécifiée en 
	 * utilisant le taux de compression spécifié. 
	 * @param zipFile L'archive zip dans laquelle seront compressés les 
	 * fichiers. Ce fichier doit déjà avoir été créé. 
	 * @param level niveau de compression. 
	 * @param files fichiers à compresser. */  
	public static final void zip(File zipFile, int level, File ...files)  throws IOException {  
	
		zip(zipFile, level, CHECK_NONE, files); 
	}  
	
	
	/** * Compresse les fichiers spécifiés dans l'archive zip spécifiée en 
	 * utilisant le checksum spécifié. 
	 * @param zipFile L'archive zip dans laquelle seront compressés les 
	 * fichiers. Ce fichier doit déjà avoir été créé. 
	 * @param checksum Le checksum à utiliser lors de la compression. 
	 * @param files fichiers à compresser. */  
	public static final void zip(File zipFile, Checksum checksum, File ...files)  throws IOException {  
	
		zip(zipFile, Deflater.BEST_COMPRESSION, checksum, files);
	}  
	
	
	/** * Compresse les fichiers spécifiés dans l'archive zip spécifiée en 
	 * utilisant le taux de compression et le checksum spécifié. 
	 * @param zipFile L'archive zip dans laquelle seront compressés les 
	 * fichiers. Ce fichier doit déjà avoir été créé. * @param level niveau de compression. 
	 * @param checksum Le checksum à utiliser lors de la compression. 
	 * @param files fichiers à compresser. */
	public static final void zip(File zipFile, int level, Checksum checksum, File ...files)  throws FileNotFoundException, IOException {
		
		FileOutputStream fos = new FileOutputStream(zipFile);
		CheckedOutputStream cos = (checksum == null) ? null : new CheckedOutputStream(fos, checksum);
		BufferedOutputStream bos = new BufferedOutputStream((cos != null) ? cos : fos);
		ZipOutputStream zos = new ZipOutputStream(bos);
		zos.setMethod(ZipOutputStream.DEFLATED); zos.setLevel(level);
	
		try {  
			for(File f : files) {  
				String path = f.getCanonicalPath(); 
				path = path.substring(0, path.lastIndexOf(File.separator)); 
				HashMap<ZipEntry, File> entries = createEntries(path, f); 
				byte[] buf = new byte[BUFFER_SIZE]; 
	
				for(Map.Entry<ZipEntry, File> e : entries.entrySet()) {  
	
					if(e.getValue().length() == 0) {  
						ZipEntry zipEntry = e.getKey(); 
						zipEntry.setSize(0); zipEntry.setCrc(0); 
						zipEntry.setMethod(ZipEntry.STORED); 
						zos.closeEntry(); zos.putNextEntry(e.getKey()); 
						continue; 
					}
					zos.putNextEntry(e.getKey()); 
					FileInputStream fis = new FileInputStream(e.getValue()); 
					BufferedInputStream bis = new BufferedInputStream(fis, BUFFER_SIZE); 
					int nbRead;
					try {  
						while((nbRead = bis.read(buf)) > 0) {
					 		zos.write(buf, 0, nbRead);
						}  
					} finally {  
						zos.closeEntry(); 
						bis.close(); 
						fis.close(); 
					}  
				}  
			}  
			zos.finish(); 
		} finally {  
	
			zos.close(); 
			bos.close(); 
			if(cos != null) {  
				cos.close(); 
			}  fos.close(); 
		}  
	}
	
	  
	private static final HashMap<ZipEntry, File> createEntries(String path, File ...files)  throws IOException {  
	
		path += path.endsWith(File.separator) ? "" : File.separator; HashMap<ZipEntry, File> map = new HashMap<ZipEntry, File>();
		for(File f : files) {
	
			if(f.isDirectory()) {
	
			  	HashMap<ZipEntry, File> temp = createEntries(path, f.listFiles());
				String entrypath = f.getAbsolutePath().replace(path, ""). replace(File.separatorChar, '/');
				entrypath += entrypath.endsWith("/") ? "" : "/";
				map.put(new ZipEntry(entrypath), f);
				for(Map.Entry<ZipEntry, File> e : temp.entrySet()) {
					map.put(e.getKey(), e.getValue());
				}
			} else {  
				map.put(new ZipEntry(f.getAbsolutePath().replace(path, ""). replace(File.separatorChar, '/')), f);
			}  
		}  
		return map; 
	} 

}

